package com.simple.rpc.proxy;

import com.simple.rpc.model.ServiceInvokeMetaData;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.Socket;

/**
 * 远程调用的传输代理
 *
 * @author mhh
 * @since 2020/9/17
 */
public class RemoteInvocationHandler implements InvocationHandler {

    /**
     * 远程地址
     */
    private String host;

    /**
     * 远程端口
     */
    private Integer port;

    public RemoteInvocationHandler(String host, Integer port) {
        this.host = host;
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Socket socket = new Socket(host, port);
        OutputStream outputStream = socket.getOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

        Class<?>[] paramTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            paramTypes[i] = args[i].getClass();
        }
        //注意proxy已经是代理类了 所以第一个参数要用method.getDeclaringClass().getName()
        ServiceInvokeMetaData serviceInvokeMetaData = new ServiceInvokeMetaData(method.getDeclaringClass().getName(), method.getName(), paramTypes, args);
        objectOutputStream.writeObject(serviceInvokeMetaData);
        return getResultFromRemote(socket);
    }


    /**
     * 从远程服务获取方法调用结果
     *
     * @param socket 通信socket
     * @return 调用结果
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private Object getResultFromRemote(Socket socket) throws IOException, ClassNotFoundException {
        InputStream inputStream = socket.getInputStream();
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        return objectInputStream.readObject();
    }
}
